package org.agilar.gwt.sortabletable;

import java.util.HashMap;
import java.util.Map;

import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.SourcesTableEvents;
import com.google.gwt.user.client.ui.TableListener;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
import com.google.gwt.user.client.ui.HTMLTable.ColumnFormatter;
import com.google.gwt.user.client.ui.HTMLTable.RowFormatter;

public class SortableTable extends SimplePanel implements SourcesTableEvents, TableListener, TableModelListener
{
	private Grid grid;
	private TableModel model;
	
	private boolean ascending = false;
	private int sortColumn = -1;

	private Map<Integer, DataFormatter> dataFormatters = new HashMap<Integer, DataFormatter>();
	
	private Image ascendingImage;
	private Image descendingImage;

	public SortableTable(int columns)
	{
		grid = new Grid(1, columns);
		grid.addTableListener(this);
	}
	
	public void addTableListener(TableListener listener)
	{
		grid.addTableListener(listener);
	}
	
	public void removeTableListener(TableListener listener)
	{
		grid.removeTableListener(listener);
	}
	
	public int getCellCount(int row)
	{
		return model.getColumnCount();
	}
	
	public CellFormatter getCellFormatter()
	{
		return grid.getCellFormatter();
	}
	
	public int getCellPadding()
	{
		return grid.getCellPadding();
	}

	public int getCellSpacing()
	{
		return grid.getCellSpacing();
	}
	
	public void setCellPadding(int padding)
	{
		grid.setCellPadding(padding);
	}
	
	public void setCellSpacing(int spacing)
	{
		grid.setCellSpacing(spacing);
	}
	
	public void setBorderWidth(int width)
	{
		grid.setBorderWidth(width);
	}
	
	public ColumnFormatter getColumnFormatter()
	{
		return grid.getColumnFormatter();
	}
	
	public String getHTML(int row, int column)
	{
		return grid.getHTML(row+1, column);
	}
	
	public int getRowCount()
	{
		return model.getRowCount();
	}
	
	public int getColumnCount()
	{
		return model.getColumnCount();
	}
	
	public RowFormatter getRowFormatter()
	{
		return grid.getRowFormatter();
	}
	
	public String getText(int row, int column)
	{
		return grid.getText(row+1, column);
	}
	
	public Widget getWidget(int row, int column)
	{
		return grid.getWidget(row+1, column);
	}
	
	public void setModel(TableModel model)
	{
		if (this.model != null)
		{
			this.model.removeTableModelListener(this);
		}
		this.model = model;
		this.model.addTableModelListener(this);
		//dataFormatters.clear();
		
		// rebuild UI
		buildTable();
	}

	private void buildTable()
	{
		buildCells();
		
		setWidget(grid);
	}

	private void formatHeader()
	{
		CellFormatter formatter = getCellFormatter();
		for (int col=0; col<model.getColumnCount(); col++)
		{
			if (model.isColumnSortable(col))
			{
				formatter.addStyleName(0, col, "agilar-sortabletable-sortableColumnHeader");
				if (col == sortColumn)
				{
					formatter.addStyleName(0, col, "agilar-sortabletable-sortableColumnHeader-"+(ascending?"ascending":"descending"));
				}
			}
			else
			{
				formatter.addStyleName(0, col, "agilar-sortabletable-columnHeader");
			}
		}
	}

	private void setColumnNames()
	{
		for (int col=0; col<model.getColumnCount(); col++)
		{
			if (col == sortColumn && ascendingImage != null && descendingImage != null)
			{
				FlowPanel panel = new FlowPanel();
				panel.add(ascending?ascendingImage:descendingImage);
				panel.add(new HTML(model.getColumnName(col)));
				grid.setWidget(0, col, panel);
			}
			else
			{
				grid.setText(0, col, model.getColumnName(col));
			}
		}
		
		formatHeader();
	}

	private void buildCells()
	{
		setColumnNames();
		
		grid.resize(model.getRowCount()+1, model.getColumnCount());
		
		for (int col=0; col<model.getColumnCount(); col++)
		{
			DataFormatter formatter = dataFormatters.get(col);
			for (int row=0; row<model.getRowCount(); row++)
			{
				Object cell = model.get(row, col);
				if (cell instanceof Widget)
				{
					grid.setWidget(row+1, col, (Widget)cell);
				}
				else
				{
					if (formatter != null)
					{
						grid.setText(row+1, col, formatter.format(cell));
					}
					else
					{
						grid.setText(row+1, col, cell.toString());
					}
				}
			}			
		}
	}
	
	public void setDataFormatter(int column, DataFormatter formatter)
	{
		dataFormatters.put(column, formatter);
	}
	
	public void removeDataFormatter(int column)
	{
		dataFormatters.remove(column);
	}

	public void onCellClicked(SourcesTableEvents sender, int row, int column)
	{
		if (row == 0 && model.isColumnSortable(column))
		{
			if (sortColumn != column)
			{
				ascending = true;
			}
			else
			{
				ascending = !ascending;
			}
			
			sort(column, ascending);
		}
	}
	
	public void sort(int column, boolean ascending)
	{
		this.sortColumn = column;
		this.ascending = ascending;
		model.sort(column, ascending);
		buildCells();
	}

	public void resort()
	{
		sort(sortColumn, ascending);
	}
	
	public void modelChanged()
	{
		buildCells();
	}

	public Image getAscendingImage()
	{
		return ascendingImage;
	}

	public void setAscendingImage(Image ascendingImage)
	{
		this.ascendingImage = ascendingImage;
	}

	public Image getDescendingImage()
	{
		return descendingImage;
	}

	public void setDescendingImage(Image descendingImage)
	{
		this.descendingImage = descendingImage;
	}
}
